using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Xml;

namespace OpenComposite.Mapper
{
	public class XsltGenerator
	{
		#region Initialization

		public XsltGenerator()
		{
			init();
		}

		private void init()
		{
		}

		#endregion

		#region Public Members

		#region Properties

		public string SourceSchemaNamespace
		{
			get { return _sSourceSchemaNamespace; }
			set {
				if ( value == null )
					throw new NullReferenceException("The SourceSchemaNamespace cannot be null.");
				_sSourceSchemaNamespace = value;
			}
		}
		public string TargetSchemaNamespace
		{
			get { return _sTargetSchemaNamespace; }
			set {
				if ( value == null )
					throw new NullReferenceException("The TargetSchemaNamespace cannot be null.");
				_sTargetSchemaNamespace = value;
			}
		}

		public string SourceUri
		{
			get { return _sSourceUri; }
			set {
				//if ( value == null )
				//    throw new NullReferenceException("The SourceUri cannot be null.");
				_sSourceUri = value;
			}
		}
		public string TargetUri
		{
			get { return _sTargetUri; }
			set {
				//if ( value == null )
				//    throw new NullReferenceException("The TargetUri cannot be null.");
				_sTargetUri = value;
			}
		}

		public TreeNode SourceRootTreeNode
		{
			get { return _tnSourceRoot; }
			set {
				if ( value == null )
					throw new NullReferenceException("The SourceRootTreeNode cannot be null.");
				_tnSourceRoot = value;
				_tnSourceRoot.TreeView.PathSeparator = "/";
			}
		}
		public TreeNode TargetRootTreeNode
		{
			get { return _tnTargetRoot; }
			set {
				if ( value == null )
					throw new NullReferenceException("The TargetRootTreeNode cannot be null.");
				_tnTargetRoot = value;
				_tnTargetRoot.TreeView.PathSeparator = "/";
			}
		}

		#endregion

		#region Methods

		public XsltDocument GenerateXsltSourceToTarget()
		{
			//if ( this.SourceUri == null )
			//    throw new NullReferenceException("The SourceUri cannot be null.");
			//if ( this.TargetUri == null )
			//    throw new NullReferenceException("The TargetUri cannot be null.");
			if ( this.SourceSchemaNamespace == null )
				throw new NullReferenceException("The SourceSchemaNamespace cannot be null.");
			if ( this.TargetSchemaNamespace == null )
				throw new NullReferenceException("The TargetSchemaNamespace cannot be null.");
			if ( this.SourceRootTreeNode == null )
				throw new NullReferenceException("The SourceRootTreeNode cannot be null.");
			if ( this.TargetRootTreeNode == null )
				throw new NullReferenceException("The TargetRootTreeNode cannot be null.");

			// delete previous xslt informations from the target tree
			clearXsltNodeInfos(_tnSourceRoot);
			clearXsltNodeInfos(_tnTargetRoot);

			// new xslt document
			XsltDocument xslt = new XsltDocument();

			// set namespace properties
			xslt.SourceSchemaNamespace = this.SourceSchemaNamespace;
			xslt.TargetSchemaNamespace = this.TargetSchemaNamespace;

			// set output format
			xslt.DocumentElement.AppendChild(xslt.CreateXsltOutput("xml", Encoding.UTF8, true));

			// create main template
			XsltTemplate xTemplate = xslt.CreateXsltTemplate(
				string.Format("/{0}:{1}", ( (TreeNodeInfos)_tnSourceRoot.Tag ).Prefix/*xslt.SourcePrefix*/,
										  _tnSourceRoot.Text));
			xslt.DocumentElement.AppendChild(xTemplate);
			( (TreeNodeInfos)_tnSourceRoot.Tag ).XsltNode = xTemplate;

			_xnsmgr = new XmlNamespaceManager(xslt.NameTable);
			_xnsmgr.AddNamespace(XsltStatics.XsltPrefix, XsltStatics.XsltNamespace);

			// add root node of target schema
			TreeNodeInfos tniTargetRoot = _tnTargetRoot.Tag as TreeNodeInfos;
			if ( tniTargetRoot == null )
				throw new Exception("Target root tag is not a TreeNodeInfos object.");
			XmlElement xeRootTarget = xslt.CreateTargetElement(tniTargetRoot.Prefix,
															   _tnTargetRoot.Text,
															   tniTargetRoot.Namespace);
			xTemplate.AppendChild(xeRootTarget);
			if ( tniTargetRoot != null )
				tniTargetRoot.XsltNode = xeRootTarget;

			// parse source tree and create xslt recursive
			generateXsltRecursive(_tnTargetRoot, xslt);

			if ( this.TargetUri != null ) {
				// add xsi:schemaLocation
				xeRootTarget.InsertBefore(xslt.CreateXsltAttribute("xsi:schemaLocation",
																  string.Format("{0} {1}",
																	this.TargetSchemaNamespace,
																	this.TargetUri)),
										  xeRootTarget.FirstChild);
			}
			try {
				System.Xml.Xsl.XslCompiledTransform transform = new System.Xml.Xsl.XslCompiledTransform();
				transform.Load(xslt);
			} catch ( Exception ex ) {
				MessageBox.Show(ex.Message, ex.Source, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			return xslt;
		}

		public XsltDocument GenerateXsltSourceToTarget2()
		{
			if ( this.SourceSchemaNamespace == null )
				throw new NullReferenceException("The SourceSchemaNamespace cannot be null.");
			if ( this.TargetSchemaNamespace == null )
				throw new NullReferenceException("The TargetSchemaNamespace cannot be null.");
			if ( this.SourceRootTreeNode == null )
				throw new NullReferenceException("The SourceRootTreeNode cannot be null.");
			if ( this.TargetRootTreeNode == null )
				throw new NullReferenceException("The TargetRootTreeNode cannot be null.");

			// delete previous xslt informations from the target tree
			clearXsltNodeInfos2(_tnSourceRoot);
			clearXsltNodeInfos2(_tnTargetRoot);

			// new xslt document
			XsltDocument xslt = new XsltDocument(( (TreeNodeInfos)_tnSourceRoot.Tag ).Prefix,
												 this.SourceSchemaNamespace,
												 ( (TreeNodeInfos)_tnTargetRoot.Tag ).Prefix,
												 this.TargetSchemaNamespace);

			//// set namespace properties
			//xslt.SourceSchemaNamespace = this.SourceSchemaNamespace;
			//xslt.TargetSchemaNamespace = this.TargetSchemaNamespace;

			// set output format
			xslt.DocumentElement.AppendChild(xslt.CreateXsltOutput("xml", Encoding.UTF8, true));
			// create main template
			XsltTemplate xTemplate = xslt.CreateXsltTemplate(
				string.Format("/{0}:{1}", ( (TreeNodeInfos)_tnSourceRoot.Tag ).Prefix/*xslt.SourcePrefix*/, _tnSourceRoot.Text));
			xTemplate.CompileInformations.SourceSchemaPath = _tnSourceRoot.FullPath;
			xslt.DocumentElement.AppendChild(xTemplate);
			( (TreeNodeInfos)_tnSourceRoot.Tag ).XsltNode = xTemplate;
			( (TreeNodeInfos)_tnSourceRoot.Tag ).XsltNode2 = xTemplate;

			_xnsmgr = new XmlNamespaceManager(xslt.NameTable);
			_xnsmgr.AddNamespace(XsltStatics.XsltPrefix, XsltStatics.XsltNamespace);

			// add root node of target schema
			XmlElement xeRootTarget;
			TreeNodeInfos tniSourceRoot = _tnSourceRoot.Tag as TreeNodeInfos;
			TreeNodeInfos tniTargetRoot = _tnTargetRoot.Tag as TreeNodeInfos;
			if ( tniTargetRoot == null ) {
				xeRootTarget = xslt.CreateTargetElement(
					xslt.TargetPrefix, _tnTargetRoot.Text, xslt.TargetSchemaNamespace);
			} else {
				xeRootTarget = xslt.CreateTargetElement(
					tniTargetRoot.Prefix, _tnTargetRoot.Text, tniTargetRoot.Namespace);
			}
			xTemplate.AppendChild(xeRootTarget);
			List<MappingItem> misParent = null;
			if ( tniTargetRoot != null ) {
				tniTargetRoot.XsltNode = xeRootTarget;
				tniTargetRoot.XsltNodes.Add(_tnSourceRoot, new XsltNodeDetails(_tnSourceRoot, xeRootTarget, xeRootTarget));
				if ( tniTargetRoot.Mappings.Count == 1 ) {
					tniTargetRoot.VariableName = _tnSourceRoot.Text;
					XsltVariable xVar = xslt.CreateXsltVariable(tniTargetRoot.VariableName, ".");
					xTemplate.CompileInformations.XsltVariableNode = xVar;
					xeRootTarget.AppendChild(xVar);
					XsltElement xnS;
					if ( tniTargetRoot.Mappings[0].SourceNode == _tnSourceRoot ) {
						xslt.AddSourceNamespace(tniSourceRoot.Prefix, tniSourceRoot.Namespace);
						xnS = xslt.CreateXsltForEach(
							string.Format("{0}:{1}", tniSourceRoot.Prefix/*xslt.SourcePrefix*/,
													 _tnSourceRoot.FirstNode.Text));
						xnS.CompileInformations.SourceSchemaPath = _tnSourceRoot.FirstNode.FullPath;
						xnS.CompileInformations.XsltSourceParentNode = xTemplate;
					} else {
						TreeNodeInfos tniTargetSourceMapping = tniTargetRoot.Mappings[0].SourceNode.Tag as TreeNodeInfos;
						xslt.AddSourceNamespace(tniTargetSourceMapping.Prefix, tniTargetSourceMapping.Namespace);
						xnS = xslt.CreateXsltForEach(string.Format("{0}:{1}",
							tniTargetSourceMapping.Prefix/*xslt.SourcePrefix*/,
							tniTargetRoot.Mappings[0].SourceNode.Text));
						xnS.CompileInformations.SourceSchemaPath = tniTargetRoot.Mappings[0].SourceNode.FullPath;
						xnS.CompileInformations.XsltSourceParentNode = xTemplate;
					}
					misParent = tniTargetRoot.Mappings;
					//((TreeNodeInfos)tnParentSource.Tag).VariableName = tniTargetRoot.VariableName;
					xeRootTarget.AppendChild(xnS);
					tniTargetRoot.XsltNodes[_tnSourceRoot].targetxmlnode = xnS;
				} else if ( tniTargetRoot.Mappings.Count > 1 ) {
					throw new Exception("It is not allowed to connect the root target element with more then one source element.");
				}
			} else
				throw new Exception("Target root tag is not a TreeNodeInfos object.");

			// parse source tree and prepare for xslt generation
			createSourceXslt(_tnSourceRoot, xslt);
			// parse target tree and create xslt recursive
			generateXsltRecursive2(_tnTargetRoot.TreeView.Nodes, misParent, xslt);

			// add xsi:schemaLocation
			xeRootTarget.InsertBefore(xslt.CreateXsltAttribute("xsi:schemaLocation",
															   string.Format("{0} {1}",
															   this.TargetSchemaNamespace,
															   this.TargetUri)),
									  xeRootTarget.FirstChild);

			List<XmlNode> lst = buildXsltRecursive2(_tnTargetRoot, xslt);
			//foreach ( XmlNode xn in lst ) {
			//    if ( xn != xeRootTarget )
			//        xeRootTarget..AppendChild(xn.V);
			//}

			checkForEachPaths(xslt);
			removeEmptyTargetElementsRecursive(null, xslt);

			try {
				System.Xml.Xsl.XslCompiledTransform transform = new System.Xml.Xsl.XslCompiledTransform();
				transform.Load(xslt);
			} catch ( Exception ex ) {
				MessageBox.Show(ex.Message, ex.Source, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			return xslt;
		}

		#endregion

		#endregion

		#region Private Members

		#region Variables

		private string _sSourceSchemaNamespace = null;
		private string _sTargetSchemaNamespace = null;

		private string _sSourceUri = null;
		private string _sTargetUri = null;

		private TreeNode _tnSourceRoot = null;
		private TreeNode _tnTargetRoot = null;

		private XmlNamespaceManager _xnsmgr = null;

		#endregion

		#region Methods

		#region GenerateXsltSourceToTarget methods

		private void clearXsltNodeInfos(TreeNode tn)
		{
			TreeNodeInfos tni = tn.Tag as TreeNodeInfos;
			if ( tni != null ) {
				tni.XsltNode = null;
			}
			foreach ( TreeNode tnChild in tn.Nodes ) {
				clearXsltNodeInfos(tnChild);
			}
		}
		private void generateXsltRecursive(TreeNode tnTarget, XsltDocument xslt)
		{
			TreeNodeInfos tniTarget = tnTarget.Tag as TreeNodeInfos;
			// for all child nodes of tnTarget
			foreach ( TreeNode tn in tnTarget.Nodes ) {
				TreeNodeInfos tni = tn.Tag as TreeNodeInfos;
				if ( tni != null && tni.Mappings.Count > 0 ) {
					// create the xslt for the mappings of the current node
					createXsltForMappings(tni.Mappings, xslt);
				}
				generateXsltRecursive(tn, xslt);
			}
		}
		private void createXsltForMappings(List<MappingItem> mappings, XsltDocument xslt)
		{
			// for each MappingItem
			foreach ( MappingItem mi in mappings ) {
				// create Xslt code for the current MappingItem
				XmlNode xnMap = createXsltForMappingItem(mi, xslt);
				// create the missing source parent elements
				XmlNode xnSource = null;
				if ( mi.SourceNode.Parent != _tnSourceRoot )
					xnSource = createParentSourceXslt(mi.SourceNode.Parent, xnMap, xslt);

				// get connected parent treenodes for the current source and target treenode
				TreeNode tnSCP = getConnectedParentNode(mi.SourceNode);
				TreeNode tnTCP = getConnectedParentNode(mi.TargetNode);
				// are these parent treenodes connected?
				TreeNodeInfos tniT = tnTCP.Tag as TreeNodeInfos;
				bool bConnected = false;
				foreach ( MappingItem miT in tniT.Mappings ) {
					if ( miT.SourceNode == tnSCP ) {
						bConnected = true;
						break;
					}
				}
				// if parent treenodes not connected
				if ( !bConnected ) {
					TreeNodeInfos tniS = tnSCP.Tag as TreeNodeInfos;
					// create variable element for source parent
					bool exists = false;
					foreach ( XmlNode xn in tniS.XsltNode.ChildNodes ) {
						XsltVariable xvar = xn as XsltVariable;
						if ( xvar != null && xvar.VariableName == tnSCP.Text ) {
							exists = true;
							break;
						}
					}
					if ( !exists ) {
						XsltVariable xVar = xslt.CreateXsltVariable(tnSCP.Text, ".");
						tniS.XsltNode.InsertAfter(xVar, null);
						if ( tniS.XsltNode is XsltElement )
							( (XsltElement)tniS.XsltNode ).CompileInformations.XsltVariableNode = xVar;
					}
					if ( xnSource != null ) {
						// add variable to for-each select attribute to get the correct path
						( (XsltForEach)xnSource ).Select =
							string.Format("${0}/{1}", tnSCP.Text, ( (XsltForEach)xnSource ).Select);
					}
				}
				// create the missing target parent elements and set new node as child
				createParentTargetXslt(mi.TargetNode.Parent, ( xnSource == null ? xnMap : xnSource ), xslt);
			}
		}
		private XmlNode createParentSourceXslt(TreeNode tnSourceParent, XmlNode xnChild, XsltDocument xslt)
		{
			if ( tnSourceParent == null )
				return xnChild;

			bool bExists = false;
			XmlNode xnNew = null;
			TreeNodeInfos tniSourceParent = tnSourceParent.Tag as TreeNodeInfos;
			if ( tniSourceParent == null )
				throw new Exception("TreeNode.Tag is not a TreeNodeInfos object. " +
					"(TreeNode.Text == " + tnSourceParent.Text + ")");

			if ( tniSourceParent.Mappings.Count == 0 || tniSourceParent.Mappings[0].TargetNode == _tnTargetRoot ) {
				string qname = string.Format("{0}:{1}", tniSourceParent.Prefix/*xslt.SourcePrefix*/,
														tnSourceParent.Text);
				xnNew = xslt.SelectSingleNode("//xsl:for-each[@select=\"" + qname + "\"]", _xnsmgr);
				
				// if no XsltNode exists create one
				if ( xnNew == null ) {
					xnNew = xslt.CreateXsltForEach(qname);
					tniSourceParent.XsltNode = xnNew as XsltElement;
				} else
					bExists = true;

				xnNew.AppendChild(xnChild);
			}
			// create the missing parent elements and set new node as child
			if ( !bExists && xnNew != null && tnSourceParent.Parent != _tnSourceRoot ) {
				XmlNode xnParent = createParentSourceXslt(tnSourceParent.Parent, xnNew, xslt);
				if ( xnParent != null )
					xnNew = xnParent;
			}
			return xnNew;
		}
		private void createParentTargetXslt(TreeNode tnTargetParent, XmlNode xnChild, XsltDocument xslt)
		{
			// Pre Conditions
			if ( tnTargetParent == null )
				return;

			bool bIsNew = false;
			TreeNodeInfos tniTargetParent = tnTargetParent.Tag as TreeNodeInfos;
			if ( tniTargetParent == null )
				throw new Exception("TreeNode.Tag is not a TreeNodeInfos object. " +
					"(TreeNode.Text == " + tnTargetParent.Text + ")");

			if ( tniTargetParent.XsltNode == null ) {
				// if no XsltNode exists create one
				tniTargetParent.XsltNode = xslt.CreateTargetElement(tniTargetParent.Prefix,
																	tnTargetParent.Text,
																	tniTargetParent.Namespace);
				bIsNew = true;
			}

			// append the child xml node
			tniTargetParent.XsltNode.AppendChild(xnChild);

			// create the missing parent elements and set new node as child
			if ( bIsNew )
				createParentTargetXslt(tnTargetParent.Parent, tniTargetParent.XsltNode, xslt);
		}
		private XmlNode createXsltForMappingItem(MappingItem mi, XsltDocument xslt)
		{
			// create a new for-each
			XsltForEach xforeach = xslt.CreateXsltForEach(string.Format("{0}:{1}", xslt.SourcePrefix, mi.SourceNode.Text));
			// create the target node
			TreeNodeInfos tniTarget = mi.TargetNode.Tag as TreeNodeInfos;
			XmlElement xnTarget = xslt.CreateTargetElement(tniTarget.Prefix,
														   mi.TargetNode.Text,
														   tniTarget.Namespace);
			if ( tniTarget != null )
				tniTarget.XsltNode = xnTarget;
			tniTarget = mi.SourceNode.Tag as TreeNodeInfos;
			if ( tniTarget != null )
				tniTarget.XsltNode = xforeach;
			xforeach.AppendChild(xnTarget);
			// if the node is a simple type, add a value-of to the xslt document
			if ( mi.TargetNode.Nodes.Count == 0 )
				xnTarget.AppendChild(xslt.CreateValueOf());

			// return the new for-each element
			return xforeach;
		}
		private TreeNode getConnectedParentNode(TreeNode tnChild)
		{
			if ( tnChild == null )
				return null;
			if ( tnChild.Parent == null )
				return tnChild;
			TreeNodeInfos tni = tnChild.Parent.Tag as TreeNodeInfos;
			if ( tni == null || tni.Mappings.Count == 0 )
				return getConnectedParentNode(tnChild.Parent);

			return tnChild.Parent;
		}

		#endregion

		#region GenerateXsltSourceToTarget2 methods

		private void clearXsltNodeInfos2(TreeNode tn)
		{
			TreeNodeInfos tni = tn.Tag as TreeNodeInfos;
			if ( tni != null ) {
				tni.XsltNode = null;
				tni.XsltNode2 = null;
				tni.XsltNodes = new Dictionary<TreeNode,XsltNodeDetails>();
			}
			foreach ( TreeNode tnChild in tn.Nodes ) {
				clearXsltNodeInfos2(tnChild);
			}
		}
		private void generateXsltRecursive2(TreeNodeCollection tnCollTarget, List<MappingItem> misParent,
											XsltDocument xslt)
		{
			//TreeNodeInfos tniTarget = tnTarget.Tag as TreeNodeInfos;
			// for all child nodes of tnTarget
			foreach ( TreeNode tn in tnCollTarget ) {
				TreeNodeInfos tni = tn.Tag as TreeNodeInfos;
				if ( tni != null && tni.Mappings.Count > 0 ) {
					// create the xslt for the mappings of the current node
					createXslt(tn, misParent, xslt);
					generateXsltRecursive2(tn.Nodes, tni.Mappings, xslt);
				} else {
					generateXsltRecursive2(tn.Nodes, misParent, xslt);
				}
			}
		}
		private void createXslt(TreeNode tnCurrent, List<MappingItem> misParent, XsltDocument xslt)
		{
			// create xslt for each mapping item
			TreeNodeInfos tniCurrent = tnCurrent.Tag as TreeNodeInfos;
			TreeNodeInfos tniParent = null;
			TreeNode tnParent = tnCurrent.Parent;
			if ( tnParent != null ) {
				tniParent = tnCurrent.Parent.Tag as TreeNodeInfos;
			}
			foreach ( MappingItem mi in tniCurrent.Mappings ) {
				// create source for-each element
				//XsltForEach xforeach = xslt.CreateXsltForEach(
				//    string.Format("{0}:{1}", xslt.SourcePrefix, mi.SourceNode.Text));
				TreeNodeInfos tniS = mi.SourceNode.Tag as TreeNodeInfos;
				if ( tniS == null )
					continue;
				// add target node
				XmlElement xeTarget;
				if ( tniCurrent.XsltNode != null ) {
					xeTarget = tniCurrent.XsltNode as XmlElement;
					tniCurrent.XsltNode2 = xeTarget;
				} else {
					xeTarget = xslt.CreateTargetElement(tniCurrent.Prefix,
														tnCurrent.Text,
														tniCurrent.Namespace);
					//xeTarget.CompileInformations.TargetNodeName = tnCurrent.Text;
					//xeTarget.CompileInformations.TargetSchemaPath = tnCurrent.FullPath;
					//xforeach.AppendChild(xeTarget);
					//if ( tniParent != null ) {
					//    if ( tniParent.XsltNode2 != null ) {
					//        tniParent.XsltNode2.AppendChild(xeTarget);
					//    } else if ( tniParent.XsltNode != null ) {
					//        tniParent.XsltNode.AppendChild(xeTarget);
					//    }
					//} else {
					if ( mi.SourceNode == _tnSourceRoot && tnParent != null ) {
						if ( tniParent.XsltNode2 != null ) {
							tniParent.XsltNode2.AppendChild(xeTarget);
						} else if ( tniParent.XsltNode != null ) {
							tniParent.XsltNode.AppendChild(xeTarget);
						}
					} else {
						if ( tniS.XsltNode2 != null ) {
							tniS.XsltNode2.AppendChild(xeTarget);
						} else if ( tniS.XsltNode != null ) {
							tniS.XsltNode.AppendChild(xeTarget);
						}
					}
					//}
				}
				// if treenode is value item create a value-of element
				if ( tnCurrent.Nodes.Count == 0 )
					xeTarget.AppendChild(xslt.CreateValueOf());

				if ( !tniCurrent.XsltNodes.ContainsKey(mi.SourceNode) )
					tniCurrent.XsltNodes.Add(mi.SourceNode,
						new XsltNodeDetails(mi.SourceNode, tniS.XsltNode/*xforeach*/, xeTarget));

				//TreeNode tnPS = getParentConnectedTreeNode(mi.SourceNode);
				//if ( misParent != null && tnPS != null ) {
				//    bool isParent = false;
				//    foreach ( MappingItem miP in misParent ) {
				//        if ( miP.SourceNode == tnPS ) {
				//            isParent = true;
				//            break;
				//        }
				//    }
				//    if ( !isParent ) {
				//        TreeNodeInfos tniPS = tnPS.Tag as TreeNodeInfos;
				//        if ( tniPS.Mappings.Count > 0 ) {
				//            TreeNodeInfos tniTPS = tniPS.Mappings[0].TargetNode.Tag as TreeNodeInfos;
				//            XsltForEach xfe = tniS.XsltNode as XsltForEach;
				//            if ( xfe != null ) {

				//            }
				//        } else if ( tnPS == _tnSourceRoot ) {

				//        }
				//    }
				//}

				//createParentXslt(tni, mi.SourceNode, xslt);
			}
			//foreach ( TreeNode tnChild in tnCurrent.Nodes ) {
			//    createXslt(tnChild, xslt);
			//}
		}
		private TreeNode getParentConnectedTreeNode(TreeNode tn)
		{
			if ( tn.Parent == null )
				return tn;
			if ( ( (TreeNodeInfos)tn.Parent.Tag ).Mappings.Count > 0 )
				return tn.Parent;
			else
				return getParentConnectedTreeNode(tn.Parent);
		}
		private void createSourceXslt(TreeNode tnCurrent, XsltDocument xslt)
		{
			foreach ( TreeNode tn in tnCurrent.Nodes ) {
				TreeNodeInfos tni = tn.Tag as TreeNodeInfos;
				if ( tni != null && tni.Mappings.Count > 0 ) {
					if ( tni.XsltNode2 == null ) {
						xslt.AddSourceNamespace(tni.Prefix, tni.Namespace);
						tni.XsltNode = /*tni.XsltNode2 =*/ xslt.CreateXsltForEach(
							string.Format("{0}:{1}", tni.Prefix/*xslt.SourcePrefix*/, tn.Text));
						if ( tni.XsltNode is XsltElement ) {
							( (XsltElement)tni.XsltNode ).CompileInformations.SourceNodeName = tn.Text;
							( (XsltElement)tni.XsltNode ).CompileInformations.SourceSchemaPath = tn.FullPath;
							TreeNodeInfos tniParent = ( tn.Parent == null ? null : tn.Parent.Tag as TreeNodeInfos );
							if ( tniParent != null )
								( (XsltElement)tni.XsltNode ).CompileInformations.XsltSourceParentNode = tniParent.XsltNode2 as XsltElement;
						}
					}
					// create the xslt for the mappings of the current node
					createSourceParentXslt(tni, tn, xslt);
				}
				createSourceXslt(tn, xslt);
			}
		}
		private void createSourceParentXslt(TreeNodeInfos tniBase, TreeNode tnCurrentSource, XsltDocument xslt)
		{
			TreeNode tnParentS = tnCurrentSource.Parent;
			TreeNode tnParentParentS = null;
			TreeNodeInfos tniParentS = null;
			TreeNodeInfos tniParentParentS = null;

			if ( tniBase == null || tnParentS == null )
				return;

			tnParentParentS = tnParentS.Parent;

			tniParentS = tnParentS.Tag as TreeNodeInfos;
			if ( tniParentS == null )
				return;

			if ( tnParentParentS != null )
				tniParentParentS = tnParentParentS.Tag as TreeNodeInfos;
			else
				return;

			if ( (tniParentS.Mappings != null && tniParentS.Mappings.Count > 0) )
				return;

			XsltElement xnParent;
			if ( tnParentParentS == _tnSourceRoot && _tnSourceRoot.FirstNode == tnParentS &&
				 tniParentParentS.Mappings.Count > 0 && tniParentS.Mappings.Count == 0 ) {
				return;
			} else {
				xslt.AddSourceNamespace(tniParentS.Prefix, tniParentS.Namespace);
				xnParent = xslt.CreateXsltForEach(
						string.Format("{0}:{1}", tniParentS.Prefix/*xslt.SourcePrefix*/, tnParentS.Text));
			}
			xnParent.CompileInformations.SourceNodeName = tnParentS.Text;
			xnParent.CompileInformations.SourceSchemaPath = tnParentS.FullPath;
			if ( tniParentParentS != null ) {
				if ( tniParentParentS.XsltNode2 != null )
					xnParent.CompileInformations.XsltSourceParentNode = tniParentParentS.XsltNode2 as XsltElement;
				else
					xnParent.CompileInformations.XsltSourceParentNode = tniParentParentS.XsltNode as XsltElement;
			}
			//if ( tniParentS.XsltNode == null )
			//    tniParentS.XsltNode = xslt.CreateXsltForEach(
			//        string.Format("{0}:{1}", xslt.SourcePrefix, tnParentS.Text));

			if ( tniParentParentS.Mappings == null || tniParentParentS.Mappings.Count == 0 ) {
				//XmlNode xn = tniParentS.XsltNode.Clone();
				xnParent.AppendChild(tniBase.XsltNode);
				tniBase.XsltNode = xnParent;
				if ( tniBase.XsltNode2 == null )
					tniBase.XsltNode2 = xnParent;
				if ( tniParentParentS != null )
					createSourceParentXslt(tniBase, tnParentS, xslt);
			} else {
				xnParent.AppendChild(tniBase.XsltNode);
				tniBase.XsltNode = xnParent;
				if ( tniBase.XsltNode2 == null )
					tniBase.XsltNode2 = xnParent;
			}
		}
		private List<XmlNode> buildXsltRecursive2(TreeNode tnCurrent, XsltDocument xslt)
		{
			TreeNodeInfos tniCurrent = tnCurrent.Tag as TreeNodeInfos;
			List<XmlNode> lst = new List<XmlNode>();
			List<XmlNode> lstChildXslts = new List<XmlNode>();
			// get xslt of child nodes
			foreach ( TreeNode tnchild in tnCurrent.Nodes ) {
				List<XmlNode> lstChild = buildXsltRecursive2(tnchild, xslt);
				if ( lstChild != null && lstChild.Count > 0 ) {
					foreach ( XmlNode xn in lstChild ) {
						bool add = true;
						if ( tniCurrent != null && tniCurrent.XsltNode != null ) {
							XmlNode xnparent = tniCurrent.XsltNode.ParentNode;
							while ( xnparent != null ) {
								if ( xnparent == xn ) {
									xnparent = null;
									add = false;
								} else {
									xnparent = xnparent.ParentNode;
								}
							}
						}
						if ( add ) lstChildXslts.Add(xn);
					}
				}
			}

			// add child xslt to xslt nodes of current node
			if ( tniCurrent.XsltNodes.Count == 0 ) {
				XmlNode xnTarget = xslt.CreateTargetElement(tniCurrent.Prefix,
															tnCurrent.Text,
															tniCurrent.Namespace);
				tniCurrent.XsltNodes.Add(tnCurrent, new XsltNodeDetails(tnCurrent, xnTarget, xnTarget));
			}
			foreach ( KeyValuePair<TreeNode, XsltNodeDetails> mapping in tniCurrent.XsltNodes ) {
				bool isTop = false;
				if ( mapping.Key == _tnSourceRoot && tniCurrent.XsltNodes.Count > 1 )
					isTop = true;
				foreach ( XmlNode xsltchild in lstChildXslts ) {
					if ( mapping.Value.targetxmlnode != null && mapping.Value.targetxmlnode != xsltchild )
						mapping.Value.targetxmlnode.AppendChild(xsltchild.Clone());
					else if ( mapping.Value.rootxmlnode != null )
						mapping.Value.rootxmlnode.AppendChild(xsltchild.Clone());
				}
				lst.Add(mapping.Value.rootxmlnode);
				if ( isTop )
					break;
			}
			return lst;
		}
		#region Old
		//private void createXsltForMappings2(List<MappingItem> mappings, XsltDocument xslt)
		//{
		//    // for each MappingItem
		//    foreach ( MappingItem mi in mappings ) {
		//        // create Xslt code for the current MappingItem
		//        XmlNode xnMap = createXsltForMappingItem2(mi, xslt);
		//        // create the missing source parent elements
		//        XmlNode xnSource = null;
		//        if ( mi.SourceNode.Parent != _tnSourceRoot )
		//            xnSource = createParentSourceXslt2(mi.SourceNode.Parent, xnMap, xslt);

		//        // get connected parent treenodes for the current source and target treenode
		//        TreeNode tnSCP = getConnectedParentNode2(mi.SourceNode);
		//        TreeNode tnTCP = getConnectedParentNode2(mi.TargetNode);
		//        // are these parent treenodes connected?
		//        TreeNodeInfos tniT = tnTCP.Tag as TreeNodeInfos;
		//        bool bConnected = false;
		//        foreach ( MappingItem miT in tniT.Mappings ) {
		//            if ( miT.SourceNode == tnSCP ) {
		//                bConnected = true;
		//                break;
		//            }
		//        }
		//        // if parent treenodes not connected
		//        if ( !bConnected ) {
		//            TreeNodeInfos tniS = tnSCP.Tag as TreeNodeInfos;
		//            // create variable element for source parent
		//            bool exists = false;
		//            foreach ( XmlNode xn in tniS.XsltNode.ChildNodes ) {
		//                XsltVariable xvar = xn as XsltVariable;
		//                if ( xvar != null && xvar.Name == tnSCP.Text ) {
		//                    exists = true;
		//                    break;
		//                }
		//            }
		//            if ( !exists )
		//                tniS.XsltNode.InsertAfter(xslt.CreateXsltVariable(tnSCP.Text, "."), null);
		//            if ( xnSource != null ) {
		//                // add variable to for-each select attribute to get the correct path
		//                ( (XsltForEach)xnSource ).Select =
		//                    string.Format("${0}/{1}", tnSCP.Text, ( (XsltForEach)xnSource ).Select);
		//            }
		//        }
		//        // create the missing target parent elements and set new node as child
		//        tniT.XsltNode2 = createParentTargetXslt2(mi.TargetNode.Parent, ( xnSource == null ? xnMap : xnSource ), xslt);
		//    }
		//}
		//private XmlNode createParentSourceXslt2(TreeNode tnSourceParent, XmlNode xnChild, XsltDocument xslt)
		//{
		//    if ( tnSourceParent == null )
		//        return xnChild;

		//    bool bExists = false;
		//    XmlNode xnNew = null;
		//    TreeNodeInfos tni = tnSourceParent.Tag as TreeNodeInfos;
		//    if ( tni == null )
		//        throw new Exception("TreeNode.Tag is not a TreeNodeInfos object. " +
		//            "(TreeNode.Text == " + tnSourceParent.Text + ")");

		//    if ( tni.Mappings.Count == 0 || tni.Mappings[0].TargetNode == _tnTargetRoot ) {
		//        string qname = string.Format("{0}:{1}", xslt.SourcePrefix, tnSourceParent.Text);
		//        xnNew = xslt.SelectSingleNode("//xsl:for-each[@select=\"" + qname + "\"]", _xnsmgr);
				
		//        // if no XsltNode exists create one
		//        if ( xnNew == null ) {
		//            xnNew = xslt.CreateXsltForEach(qname);
		//            tni.XsltNode = xnNew;
		//        } else
		//            bExists = true;

		//        xnNew.AppendChild(xnChild);
		//    }
		//    // create the missing parent elements and set new node as child
		//    if ( !bExists && xnNew != null && tnSourceParent.Parent != _tnSourceRoot ) {
		//        XmlNode xnParent = createParentSourceXslt2(tnSourceParent.Parent, xnNew, xslt);
		//        if ( xnParent != null )
		//            xnNew = xnParent;
		//    }
		//    return xnNew;
		//}
		//private XmlNode createParentTargetXslt2(TreeNode tnTargetParent, XmlNode xnChild, XsltDocument xslt)
		//{
		//    // Pre Conditions
		//    if ( tnTargetParent == null )
		//        return null;

		//    bool bIsNew = false;
		//    TreeNodeInfos tni = tnTargetParent.Tag as TreeNodeInfos;
		//    if ( tni == null )
		//        throw new Exception("TreeNode.Tag is not a TreeNodeInfos object. " +
		//            "(TreeNode.Text == " + tnTargetParent.Text + ")");

		//    if ( tni.XsltNode == null ) {
		//        // if no XsltNode exists create one
		//        tni.XsltNode = xslt.CreateTargetElement(tnTargetParent.Text);
		//        bIsNew = true;
		//    }

		//    // append the child xml node
		//    tni.XsltNode.AppendChild(xnChild);

		//    // create the missing parent elements and set new node as child
		//    if ( bIsNew )
		//        return createParentTargetXslt2(tnTargetParent.Parent, tni.XsltNode, xslt);
		//    else
		//        return xnChild;
		//}
		//private XmlNode createXsltForMappingItem2(MappingItem mi, XsltDocument xslt)
		//{
		//    // create a new for-each
		//    XsltForEach xforeach = xslt.CreateXsltForEach(string.Format("{0}:{1}", xslt.SourcePrefix, mi.SourceNode.Text));
		//    // create the target node
		//    XmlElement xnTarget = xslt.CreateTargetElement(mi.TargetNode.Text);
		//    TreeNodeInfos tni = mi.TargetNode.Tag as TreeNodeInfos;
		//    if ( tni != null )
		//        tni.XsltNode = xnTarget;
		//    tni = mi.SourceNode.Tag as TreeNodeInfos;
		//    if ( tni != null )
		//        tni.XsltNode = xforeach;
		//    xforeach.AppendChild(xnTarget);
		//    // if the node is a simple type, add a value-of to the xslt document
		//    if ( mi.TargetNode.Nodes.Count == 0 )
		//        xnTarget.AppendChild(xslt.CreateValueOf());

		//    // return the new for-each element
		//    return xforeach;
		//}
		//private TreeNode getConnectedParentNode2(TreeNode tnChild)
		//{
		//    if ( tnChild == null )
		//        return null;
		//    if ( tnChild.Parent == null )
		//        return tnChild;
		//    TreeNodeInfos tni = tnChild.Parent.Tag as TreeNodeInfos;
		//    if ( tni == null || tni.Mappings.Count == 0 )
		//        return getConnectedParentNode2(tnChild.Parent);

		//    return tnChild.Parent;
		//}
		#endregion

		private void checkForEachPaths(XsltDocument xslt)
		{
			XsltElement xeTemplate = xslt.SelectSingleNode(
				string.Format("//{0}:template", XsltStatics.XsltPrefix), _xnsmgr) as XsltElement;
			if ( xeTemplate == null )
				throw new NullReferenceException("XSLT Document contains no template element.");

			foreach ( XmlNode xnChild in xeTemplate.ChildNodes ) {
				checkForEachPathsRecursive(xnChild, xeTemplate, xslt);
			}
		}
		private void checkForEachPathsRecursive(XmlNode xn, XsltElement xParentLoop, XsltDocument xslt)
		{
			if ( xn.NamespaceURI == XsltStatics.XsltNamespace && xn.LocalName == XsltForEach.XsltElementName) {
				XsltForEach xforeach = xn as XsltForEach;
				if ( !xforeach.CompileInformations.SourceSchemaPath.StartsWith(
						xParentLoop.CompileInformations.SourceSchemaPath) ) {
					XsltElement xPSource = xforeach.CompileInformations.XsltSourceParentNode;
					XsltElement xPPSource = getParentXsltForEach(xPSource);
					if ( xPSource != null ) {
						if ( xPSource.CompileInformations.XsltVariableNode == null ) {
							XsltVariable xVar = xslt.CreateXsltVariable(
								xPSource.CompileInformations.SourceNodeName,
								string.Format("./{0}:{1}", xPSource.Prefix/*xslt.SourcePrefix*/,
														   xPSource.CompileInformations.SourceNodeName));
							XsltForEach xfeParent = getParentXsltForEach(xParentLoop);
							if ( xfeParent != null )
								xfeParent.InsertAfter(xVar, null);
							else
								xPPSource.InsertAfter(xVar, null);
							xPSource.CompileInformations.XsltVariableNode = xVar;
						}
						xforeach.Select = string.Format("${0}/{1}",
							xPSource.CompileInformations.XsltVariableNode.VariableName, xforeach.Select);
					}
				}
				// HACK: correct wrong xslt generation
				#region description for HACK: correct wrong xslt generation
				/*
				 * WRONG:
				 *	<...>
				 *		<xslt:foreach ... />
				 *		<target_node>
				 *			<xslt:value_of ... />
				 *		</target_node>
				 *	</...>
				 * CORRECT:
				 *	<...>
				 *		<xslt:foreach ...>
				 *			<target_node>
				 *				<xslt:value_of ... />
				 *			</target_node>
				 *		</xslt:foreach>
				 *	</...>
				*/
				#endregion
				if ( xforeach.ChildNodes.Count == 0 &&
					 xforeach.NextSibling != null && xforeach.NextSibling.Prefix != xforeach.Prefix ) {
					XmlNode xnNext = xforeach.NextSibling;
					xnNext.ParentNode.RemoveChild(xnNext);
					xforeach.AppendChild(xnNext);
				}
				foreach ( XmlNode xnChild in xforeach.ChildNodes ) {
					checkForEachPathsRecursive(xnChild, xforeach, xslt);
				}
			} else {
				foreach ( XmlNode xnChild in xn.ChildNodes ) {
					checkForEachPathsRecursive(xnChild, xParentLoop, xslt);
				}
			}
		}
		private XsltForEach getParentXsltForEach(XmlNode xn)
		{
			if ( xn == null )
				return null;
			XsltForEach xforeach = xn.ParentNode as XsltForEach;
			if ( xforeach != null )
				return xforeach;
			else if ( xn.ParentNode == null )
				return null;
			else
				return getParentXsltForEach(xn.ParentNode);
		}

		private void removeEmptyTargetElementsRecursive(XmlNode xnCurrent, XsltDocument xslt)
		{
			List<XmlNode> lstRemove = new List<XmlNode>();
			XmlNodeList xnlCurrent = ( xnCurrent == null ? xslt.ChildNodes : xnCurrent.ChildNodes );
			foreach ( XmlNode xn in xnlCurrent ) {
				if ( xn.Prefix == xslt.TargetPrefix && !xn.HasChildNodes ) {
					lstRemove.Add(xn);
				} else {
					removeEmptyTargetElementsRecursive(xn, xslt);
				}
			}
			foreach ( XmlNode xn in lstRemove ) {
				if ( xnCurrent != null ) {
					xnCurrent.RemoveChild(xn);
				}
			}
		}
		#endregion

		#endregion

		#endregion

	}
}
